package patmat

import org.scalatest.FunSuite

import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner

import patmat.Huffman._

@RunWith(classOf[JUnitRunner])
class HuffmanSuite extends FunSuite {
  trait TestTrees {
    val t1 = Fork(Leaf('a', 2), Leaf('b', 3), List('a', 'b'), 5)
    val t2 = Fork(Fork(Leaf('a', 2), Leaf('b', 3), List('a', 'b'), 5), Leaf('d', 4), List('a', 'b', 'd'), 9)
  }

  trait TestLists {
    val l1 = List('a', 'b', 'd', 'a', 'a')
    val l2 = List(('d', 2), ('a', 5), ('b', 10), ('c', 8), ('f', 1), ('e', 8))
    val l3 = List[CodeTree](Leaf('a', 2), Leaf('c', 5), Leaf('b', 10), Leaf('e', 3), Leaf('D', 1))
    val l4 = List(('a', List(1, 0)), ('b', List(0, 1)), ('c', List(0, 1, 1)))
  }

  test("weight of a larger tree") {
    new TestTrees {
      assert(weight(t1) === 5)
    }
  }

  test("chars of a larger tree") {
    new TestTrees {
      assert(chars(t2) === List('a', 'b', 'd'))
    }
  }

  test("string2chars(\"hello, world\")") {
    assert(string2Chars("hello, world") === List('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd'))
  }

  test("makeOrderedLeafList for some frequency table") {
    assert(makeOrderedLeafList(List(('t', 2), ('e', 1), ('x', 3))) === List(Leaf('e', 1), Leaf('t', 2), Leaf('x', 3)))
  }

  test("combine of some leaf list") {
    val leaflist = List(Leaf('e', 1), Leaf('t', 2), Leaf('x', 4))
    assert(combine(leaflist) === List(Fork(Leaf('e', 1), Leaf('t', 2), List('e', 't'), 3), Leaf('x', 4)))
  }

  test("combine of singleton or nil") {
    val leaflist = List(Leaf('e', 1))
    combine(leaflist)
    assert(true)
    combine(Nil)
    assert(true)
  }

  test("decode and encode a very short text should be identity") {
    new TestTrees {
      assert(decode(t1, encode(t1)("ab".toList)) === "ab".toList)
    }
  }

  test("times") {
    new TestLists {
      val list: List[(Char, Int)] = times(l1)
      val index: Int = list.indexWhere(l => l._1 == 'a')
      assert(list(index)._2 === 3)
    }
  }

  test("times of nil") {
    new TestLists {
      times(Nil)
      assert(true)
    }
  }

  test("makeLeafList") {
    new TestLists {
      val leafs: List[Leaf] = makeLeafList(l2, List())
      assert(leafs.size === 6)
    }
  }

  test("makeOrderedLeafList") {
    new TestLists {
      val leafs: List[Leaf] = makeOrderedLeafList(l2)
      assert(leafs === List(Leaf('f', 1), Leaf('d', 2), Leaf('a', 5), Leaf('e', 8), Leaf('c', 8), Leaf('b', 10)))
    }
  }

  test("until") {
    new TestLists {
      val single: List[CodeTree] = until(singleton, combine)(l3)
      assert(single.length === 1)
      assert(weight(single(0)) === 21)
    }
  }

  test("createCodeTree") {
    new TestLists {
      val tree: CodeTree = createCodeTree(l1)
      assert(weight(tree) === 5)
      assert(chars(tree) === List('d', 'b', 'a'))
    }
  }

  test("encode") {
    assert(encode(frenchCode)(List('h', 'u', 'f', 'f', 'm', 'a', 'n', 'e', 's', 't', 'c', 'o', 'o', 'l')) === secret)
  }

  test("codeBits") {
    new TestLists {
      assert(codeBits(l4)('b') === List(0, 1))
      assert(codeBits(l4)('d') === Nil)
    }
  }

  test("Convert") {
    new TestTrees {
      assert(convert(t1).length === 2)
      assert(convert(t2).length === 3)
    }
  }

  test("quickEncode") {
    assert(quickEncode(frenchCode)(List('h', 'u', 'f', 'f', 'm', 'a', 'n', 'e', 's', 't', 'c', 'o', 'o', 'l')) === secret)
  }
}
